package com.maigao.service;

import com.maigao.entity.enums.Week;
import com.maigao.entity.form.AddClassRecordForm;
import com.maigao.persistent.ClassRecordRepository;
import com.maigao.persistent.CoachRepository;
import com.maigao.persistent.StudentCourseRepository;
import com.maigao.persistent.StudentRepository;
import com.maigao.util.CollectionUtils;
import com.maigao.util.DateUtils;
import com.maigao.util.WeekUtils;
import com.maigao.util.context.Context;
import com.maigao.util.context.user.User;
import com.google.common.collect.Lists;
import com.maigao.entity.jpa.*;
import com.maigao.entity.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.TextStyle;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @program: maigao
 * @description:
 * @author: yongza
 * @create: 2024-10-22 23:18
 **/
@Service
public class ClassRecordService {

    @Autowired
    private ClassRecordRepository classRecordRepository;
    @Autowired
    private StudentCourseRepository studentCourseRepository;
    @Autowired
    private CoachRepository coachRepository;
    @Autowired
    private StudentRepository studentRepository;

    public Pair<Boolean, String> revokeClassRecord(Integer recordId) {
        ClassRecord classRecord = classRecordRepository.findById(recordId);
        if (null == classRecord) {
            return Pair.of(false, "未查询到排课记录!");
        }
        if (classRecord.getStatus() == ClassRecord.Status.ABANDON.getValue()) {
            return Pair.of(false, "当前状态已经撤销!");
        }
        if (classRecord.getStatus() == ClassRecord.Status.FINISHED.getValue()) {
            return Pair.of(false, "当前课程已经完成, 不允许取消!");
        }
        classRecord.setStatus(ClassRecord.Status.ABANDON.getValue());
        classRecord.setNote("主动撤销排课记录!");
        classRecordRepository.save(classRecord);
        return Pair.of(true, null);
    }

    public Pair<Boolean, String> addClassRecord(AddClassRecordForm addClassRecordForm) {
        Integer studentCourseId = addClassRecordForm.getStudentCourseId();
        Integer studentId = addClassRecordForm.getStudentId();
        String studentName = addClassRecordForm.getStudentName();
        Integer coachId = addClassRecordForm.getCoachId();
        String coachName = addClassRecordForm.getCoachName();
        Integer scheduleId = addClassRecordForm.getScheduleId();
        String scheduleName = addClassRecordForm.getScheduleName();
        String classFromTime = addClassRecordForm.getClassFromTime();
        String classEndTime = addClassRecordForm.getClassEndTime();
        Integer classHour = null == addClassRecordForm.getClassHour() ? 1 : addClassRecordForm.getClassHour();
        if (StringUtils.isAnyBlank(studentName, coachName, scheduleName, classFromTime, classEndTime)
                || null == studentCourseId || null == studentId || null == coachId || null == scheduleId) {
            return Pair.of(false, "参数不完整!");
        }
        StudentCourse studentCourse = studentCourseRepository.findById(studentCourseId);
        if (null == studentCourse) {
            return Pair.of(false, "当前学生购买课程记录不存在!");
        }
        if (studentCourse.getRemainCount() < classHour) {
            return Pair.of(false, "当前学生课程余额不足!");
        }
        String courseType = studentCourse.getCourseType();
        int maxCount = Integer.parseInt(courseType.substring(courseType.length() - 1));
        LocalDateTime fromTime = DateUtils.parse(classFromTime, DateUtils.Formatter.DATETIME_YYYY_MM_DD_HH_MM);
        LocalDateTime endTime = DateUtils.parse(classEndTime, DateUtils.Formatter.DATETIME_YYYY_MM_DD_HH_MM);
        if (fromTime.isBefore(LocalDateTime.now()) || endTime.isBefore(LocalDateTime.now())) {
            return Pair.of(false, "上课时间已过, 不能排课!");
        }
        List<ClassRecord> existClass = classRecordRepository.findClassRecordByCoachAndTime(coachId, fromTime, endTime,
                fromTime, endTime, ClassRecord.Status.CREATED.getValue());
        if (CollectionUtils.isNotEmpty(existClass)) {
            if (existClass.stream().anyMatch(x -> !StringUtils.equals(x.getCourseType(), courseType))
                    || existClass.size() + classHour > maxCount) {
                return Pair.of(false, "该教练当前时间段已经满课!");
            }
        }
        List<ClassRecord> studentClassRecords = classRecordRepository.findByStudentIdAndStatusIn(studentId, Lists.newArrayList(ClassRecord.Status.CREATED.getValue(), ClassRecord.Status.FINISHED.getValue()));
        if (CollectionUtils.isNotEmpty(studentClassRecords)) {
            if (!studentClassRecords.stream().allMatch(x ->
                    (endTime.isBefore(x.getClassFromTime()) || endTime.isEqual(x.getClassFromTime()))
                            || (fromTime.isAfter(x.getClassEndTime()) || fromTime.isEqual(x.getClassEndTime())))) {
                return Pair.of(false, "该学员当前时间段有冲突课程!");
            }
        }
        List<ClassRecord> createdClassRecords = classRecordRepository.findByStudentIdAndStatusInAndStudentCourseId(studentId, Lists.newArrayList(ClassRecord.Status.CREATED.getValue(), ClassRecord.Status.FINISHED.getValue()), studentCourseId);
        if (CollectionUtils.isNotEmpty(createdClassRecords)) {
            if (createdClassRecords.size() + classHour > studentCourse.getRemainCount()) {
                return Pair.of(false, "该学员课程余额不足!");
            }
        }
        ClassRecord record = new ClassRecord();
        record.setStudentId(studentId);
        record.setStudentName(studentName);
        record.setCoachId(coachId);
        record.setCoachName(coachName);
        record.setStudentCourseId(studentCourseId);
        record.setCourseType(studentCourse.getCourseType());
        record.setScheduleId(scheduleId);
        record.setScheduleName(scheduleName);
        record.setClassFromTime(fromTime);
        record.setClassEndTime(endTime);
        record.setClassHour(classHour);
        record.setStatus(ClassRecord.Status.CREATED.getValue());
        record.setCreateTime(LocalDateTime.now());
        record.setCoursePrice(studentCourse.getUnitPrice().multiply(new BigDecimal(classHour)));
        classRecordRepository.save(record);
        return Pair.of(true, "");
    }

    public Pair<Boolean, String> finishClassRecords(List<Integer> classRecordIds) {
        if (CollectionUtils.isEmpty(classRecordIds)) {
            return Pair.of(false, "参数有误!");
        }
        List<ClassRecord> classRecords = classRecordRepository.findByIdIn(classRecordIds);
        if (CollectionUtils.isEmpty(classRecords)) {
            return Pair.of(false, "当前课表不存在!");
        }
        for (ClassRecord classRecord : classRecords) {
            int status = classRecord.getStatus();
            ClassRecord.Status statusEnum = ClassRecord.Status.getByValue(status);
            if (statusEnum == ClassRecord.Status.FINISHED) {
                return Pair.of(false, "当前状态已经是完成!");
            }
            if (statusEnum == ClassRecord.Status.ABANDON) {
                return Pair.of(false, "当前状态(已取消)不允许修改!");
            }
            classRecord.setStatus(ClassRecord.Status.FINISHED.getValue());
            classRecordRepository.save(classRecord);
            int studentCourseId = classRecord.getStudentCourseId();
            StudentCourse studentCourse = studentCourseRepository.findById(studentCourseId);
            studentCourse.setRemainCount(studentCourse.getRemainCount() - classRecord.getClassHour());
            studentCourseRepository.save(studentCourse);
        }
        return Pair.of(true, "");
    }

    public Triple<Boolean, String, List<ClassRecord>> findCoachFinishedClass(String coachId, String uid, String fromTime, String endTime) {
        Coach coach;
        if (null == coachId) {
            coach = coachRepository.findByUserUid(uid);
        } else {
            coach = coachRepository.findByUserUid(coachId);
        }
        if (null == coach) {
            return Triple.of(false, "当前用户没有教练身份", null);
        }
        LocalDate fromDate = DateUtils.parseDate(fromTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDate endDate = DateUtils.parseDate(endTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDateTime from = LocalDateTime.of(fromDate, LocalTime.MIN);
        LocalDateTime end = LocalDateTime.of(endDate, LocalTime.MAX);
        List<ClassRecord> classRecords = classRecordRepository.findByCoachIdAndStatusAndCreateTimeBetween(coach.getId(), ClassRecord.Status.FINISHED.getValue(), from, end);
        if (CollectionUtils.isEmpty(classRecords)) {
            return Triple.of(false, "当前教练没有上课记录!", null);
        }
        return Triple.of(true, "", classRecords);
    }

    public Triple<Boolean, String, List<CoachClassRecordDetailVO>> findCoachFinishedClassRecordDetail(String coachId, String fromTime, String endTime) {
        Coach coach = coachRepository.findByUserUid(coachId);
        if (null == coach) {
            return Triple.of(false, "未查询到教练信息", null);
        }
        LocalDate fromDate = DateUtils.parseDate(fromTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDate endDate = DateUtils.parseDate(endTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDateTime fromDateTime = LocalDateTime.of(fromDate, LocalTime.MIN);
        LocalDateTime endDateTime = LocalDateTime.of(endDate, LocalTime.MAX);

        List<ClassRecord> classRecords = classRecordRepository.findByCoachIdAndStatusAndCreateTimeBetween(coach.getId(),
                ClassRecord.Status.FINISHED.getValue(), fromDateTime, endDateTime);
        if (CollectionUtils.isEmpty(classRecords)) {
            return Triple.of(false, "当前教练没有上课记录!", null);
        }
        List<CoachClassRecordDetailVO> coachClassRecordDetailVOS = new ArrayList<>();
        Map<String, List<ClassRecord>> classRecordMap = classRecords.stream().collect(Collectors.groupingBy(x -> DateUtils.format(x.getClassFromTime(), DateUtils.Formatter.DATE_YYYY_MM_DD)));
        classRecordMap.forEach((k, v) -> {
            CoachClassRecordDetailVO item = new CoachClassRecordDetailVO();
            ClassRecord record = v.get(0);
            item.setCoachId(record.getCoachId());
            item.setCoachName(record.getCoachName());
            item.setClassDate(k);
            item.setDayOfWeek(record.getClassFromTime().getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.getDefault()));
            List<CoachClassRecordDetailVO.ClassRecordDetail> classRecordDetailList = new ArrayList<>();
            v.forEach(y -> {
                CoachClassRecordDetailVO.ClassRecordDetail classRecordDetail = new CoachClassRecordDetailVO.ClassRecordDetail();
                classRecordDetail.setStudentId(y.getStudentId());
                classRecordDetail.setStudentName(y.getStudentName());
                classRecordDetail.setCourseType(y.getCourseType());
                classRecordDetail.setClassHour(y.getClassHour());
                String classTimeRange = String.format("%s-%s", DateUtils.format(y.getClassFromTime(), DateUtils.Formatter.TIME_HH_mm),
                        DateUtils.format(y.getClassEndTime(), DateUtils.Formatter.TIME_HH_mm));
                classRecordDetail.setClassTimeRange(classTimeRange);
                classRecordDetail.setCoursePrice(y.getCoursePrice());
                classRecordDetail.setUnitPrice(y.getCoursePrice().divide(new BigDecimal(y.getClassHour())));
            });
            item.setClassRecordDetailList(classRecordDetailList);
            coachClassRecordDetailVOS.add(item);
        });
        return Triple.of(true, "", coachClassRecordDetailVOS);
    }

    public Triple<Boolean, String, List<ClassRecord>> findScheduledClass(Integer week) {
        Coach coach = getCurrentCoach();
        if (null == coach) {
            return Triple.of(false, "未获取到教练信息!", null);
        }
        Week weekEnum = Week.getByValue(week);
        if (null == weekEnum) {
            return Triple.of(false, "非法参数!", null);
        }
        Pair<LocalDateTime, LocalDateTime> weekRange = WeekUtils.getWeekRange(weekEnum);
        List<Integer> status = Arrays.asList(ClassRecord.Status.CREATED.getValue(), ClassRecord.Status.FINISHED.getValue());
        List<ClassRecord> classRecords = classRecordRepository.findByCoachIdAndStatusInAndCreateTimeBetween(coach.getId(),
                status, weekRange.getLeft(), weekRange.getRight());
        if (CollectionUtils.isEmpty(classRecords)) {
            return Triple.of(false, "未获取到排课记录!", null);
        }
        return Triple.of(true, "", classRecords);
    }

    public Triple<Boolean, String, Long> countFinishedClass() {
        Coach coach = getCurrentCoach();
        if (null == coach) {
            return Triple.of(false, "未获取到教练信息!", null);
        }
        long total = classRecordRepository.countByCoachIdAndStatus(coach.getId(), ClassRecord.Status.FINISHED.getValue());
        return Triple.of(true, "", total);
    }

    public Triple<Boolean, String, List<ClassRecordDetailVO>> findWeekClassRecord(Integer week) {
        Coach coach = getCurrentCoach();
        if (null == coach) {
            return Triple.of(false, "未获取到教练信息!", null);
        }
        return findWeekClassRecord(coach, week);
    }

    public Triple<Boolean, String, List<ClassRecordDetailVO>> findWeekClassRecordByCoachId(Integer coachId, Integer week) {
        Coach coach = coachRepository.findById(coachId);
        if (null == coach) {
            return Triple.of(false, "未获取到教练信息!", null);
        }
        return findWeekClassRecord(coach, week);
    }

    private Triple<Boolean, String, List<ClassRecordDetailVO>> findWeekClassRecord(Coach coach, Integer week) {
        Week weekEnum = Week.getByValue(week);
        if (null == weekEnum) {
            return Triple.of(false, "非法参数!", null);
        }
        Pair<LocalDateTime, LocalDateTime> weekRange = WeekUtils.getWeekRange(weekEnum);
        List<Integer> statusList = Lists.newArrayList(ClassRecord.Status.CREATED.getValue(), ClassRecord.Status.FINISHED.getValue());
        List<ClassRecord> classRecords = classRecordRepository
                .findByCoachIdAndClassFromTimeBetweenAndStatusInOrderByClassFromTime(coach.getId(), weekRange.getLeft(),
                        weekRange.getRight(), statusList);
        if (CollectionUtils.isEmpty(classRecords)) {
            return Triple.of(false, "未获取到相关数据!", null);
        }
        DateUtils.Formatter formatter = DateUtils.Formatter.DATETIME_YYYY_MM_DD_HH_MM_SS;
        Map<String, List<ClassRecord>> classRecordsMap = classRecords.stream().collect(Collectors.groupingBy(x -> DateUtils.format(x.getClassFromTime(), formatter)
                + "|" + DateUtils.format(x.getClassEndTime(), formatter)));
        Set<String> keys = classRecordsMap.keySet();
        List<ClassRecordDetailVO> classRecordDetailVOS = new ArrayList<>();
        keys.forEach(x -> {
            List<ClassRecord> sameClassRecords = classRecordsMap.get(x);
            ClassRecord firstClassRecord = sameClassRecords.get(0);
            ClassRecordDetailVO classRecordDetailVO = new ClassRecordDetailVO();
            classRecordDetailVO.setCoachName(firstClassRecord.getCoachName());
            classRecordDetailVO.setCourseType(firstClassRecord.getCourseType());
            classRecordDetailVO.setClassFromTime(DateUtils.format(firstClassRecord.getClassFromTime(), formatter));
            classRecordDetailVO.setClassEndTime(DateUtils.format(firstClassRecord.getClassEndTime(), formatter));
            List<ClassRecordDetailVO.StudentInfo> studentInfoList = new ArrayList<>();
            for (ClassRecord cr : sameClassRecords) {
                ClassRecordDetailVO.StudentInfo studentInfo = new ClassRecordDetailVO.StudentInfo();
                studentInfo.setClassRecordId(cr.getId());
                studentInfo.setStudentId(cr.getStudentId());
                studentInfo.setStudentName(cr.getStudentName());
                studentInfo.setStatus(cr.getStatus());
                studentInfo.setCreateTime(DateUtils.format(cr.getCreateTime(), DateUtils.Formatter.DATETIME_YYYY_MM_DD_HH_MM_SS));
                studentInfoList.add(studentInfo);
            }
            classRecordDetailVO.setStudentInfoList(studentInfoList);
            classRecordDetailVOS.add(classRecordDetailVO);
        });
        classRecordDetailVOS.sort((o1, o2) -> {
            LocalDateTime classFromTime1 = DateUtils.parse(o1.getClassFromTime(), formatter);
            LocalDateTime classFromTime2 = DateUtils.parse(o2.getClassFromTime(), formatter);
            if (classFromTime1.isBefore(classFromTime2)) {
                return -1;
            } else if (classFromTime1.isAfter(classFromTime2)) {
                return 1;
            }
            return 0;
        });
        return Triple.of(true, "", classRecordDetailVOS);
    }

    public Triple<Boolean, String, List<ClassRecordVO>> findStudentClassRecords(List<Integer> studentIds, String fromTime, String endTime) {
        if (CollectionUtils.isEmpty(studentIds)) {
            return Triple.of(false, "未获取到学生信息!", null);
        }
        LocalDate fromDate = DateUtils.parseDate(fromTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDate endDate = DateUtils.parseDate(endTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDateTime fromDateTime = LocalDateTime.of(fromDate, LocalTime.MIN);
        LocalDateTime endDateTime = LocalDateTime.of(endDate, LocalTime.MAX);
        List<ClassRecord> classRecords = classRecordRepository.findByStudentIdInAndCreateTimeBetweenOrderByClassFromTimeDesc(studentIds, fromDateTime, endDateTime);
        if (CollectionUtils.isEmpty(classRecords)) {
            return Triple.of(false, "未查询到排课记录!", null);
        }
        return Triple.of(true, "", convert2Detail(classRecords));
    }

    public Triple<Boolean, String, CoachClassRecordSummaryVO> findCoachClassRecordSummary(String fromTime, String endTime) {
        LocalDate fromDate = DateUtils.parseDate(fromTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDate endDate = DateUtils.parseDate(endTime, DateUtils.Formatter.DATE_YYYY_MM_DD);
        LocalDateTime fromDateTime = LocalDateTime.of(fromDate, LocalTime.MIN);
        LocalDateTime endDateTime = LocalDateTime.of(endDate, LocalTime.MAX);
        int status = ClassRecord.Status.FINISHED.getValue();
        List<ClassRecord> classRecords = classRecordRepository.findByStatusAndClassFromTimeBetween(status, fromDateTime, endDateTime);
        if (CollectionUtils.isEmpty(classRecords)) {
            return Triple.of(false, "未查询到上课记录!", null);
        }
        List<CoachClassRecordSummaryVO.CoachClassRecords> coachRecords = new ArrayList<>();
        Map<String, List<ClassRecord>> recordMap = classRecords.stream().collect(Collectors.groupingBy(ClassRecord::getCoachName));
        recordMap.forEach((k, v) -> {
            CoachClassRecordSummaryVO.CoachClassRecords classRecord = new CoachClassRecordSummaryVO.CoachClassRecords();
            classRecord.setCoachId(v.get(0).getCoachId());
            classRecord.setCoachName(k);
            classRecord.setTotalRecords(v.size());
            classRecord.setTotalMoney(v.stream().map(ClassRecord::getCoursePrice).reduce(BigDecimal.ZERO, BigDecimal::add));
            coachRecords.add(classRecord);
        });
        CoachClassRecordSummaryVO vo = new CoachClassRecordSummaryVO();
        vo.setTotalRecords(classRecords.size());
        vo.setTotalMoney(classRecords.stream().map(ClassRecord::getCoursePrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        vo.setCoachRecords(coachRecords);

        return Triple.of(true, "", vo);
    }

    public Triple<Boolean, String, List<ClassRecordVO>> findStudentClassRecords(String fromTime, String endTime) {
        List<Student> students = getCurrentStudents();
        if (CollectionUtils.isEmpty(students)) {
            return Triple.of(false, "未获取到学生信息!", null);
        }
        List<Integer> studentIds = students.stream().map(Student::getId).collect(Collectors.toList());
        return findStudentClassRecords(studentIds, fromTime, endTime);
    }

    private List<ClassRecordVO> convert2Detail(List<ClassRecord> classRecords) {
        List<ClassRecordVO> classRecordVOS = new ArrayList<>();
        for (ClassRecord cr : classRecords) {
            ClassRecordVO classRecordVO = new ClassRecordVO();
            BeanUtils.copyProperties(cr, classRecordVO);
            classRecordVO.setCreateTime(DateUtils.format(cr.getCreateTime(), DateUtils.Formatter.DATETIME_YYYY_MM_DD_HH_MM_SS));
            classRecordVO.setClassFromTime(DateUtils.format(cr.getClassFromTime(), DateUtils.Formatter.DATETIME_YYYY_MM_DD_HH_MM_SS));
            classRecordVO.setClassEndTime(DateUtils.format(cr.getClassEndTime(), DateUtils.Formatter.DATETIME_YYYY_MM_DD_HH_MM_SS));
            String dayOfWeek = cr.getClassFromTime().getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.getDefault());
            classRecordVO.setDayOfWeek(dayOfWeek);
            classRecordVOS.add(classRecordVO);
            List<QueryTakeOffRecord> takeOffRecords = cr.getTakeOffRecords();
            if (CollectionUtils.isNotEmpty(takeOffRecords)) {
                List<TakeOffRecordVO> takeOffRecordVOList = new ArrayList<>();
                for (QueryTakeOffRecord takeOffRecord : takeOffRecords) {
                    TakeOffRecordVO takeOffRecordVO = new TakeOffRecordVO();
                    BeanUtils.copyProperties(takeOffRecord, takeOffRecordVO);
                    takeOffRecordVOList.add(takeOffRecordVO);
                }
                classRecordVO.setTakeOffRecords(takeOffRecordVOList);
            }
        }
        return classRecordVOS;
    }

    private Coach getCurrentCoach() {
        User user = Context.current().getUser();
        String uid = user.getUid();
        return coachRepository.findByUserUid(uid);
    }

    private List<Student> getCurrentStudents() {
        User user = Context.current().getUser();
        String uid = user.getUid();
        return studentRepository.findByUserUid(uid);
    }

    public static void main(String[] args) {
        System.out.println(LocalDateTime.now().getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.getDefault()));
    }
}
